package cn.sgy.infrastructure.web.xss.config;

import cn.hutool.core.collection.CollUtil;
import cn.sgy.infrastructure.web.xss.business.properties.XssProperties;
import cn.sgy.infrastructure.web.xss.business.get.XssFilter;
import cn.sgy.infrastructure.web.xss.business.post.XssStringJsonDeserializer;
import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author sgy
 * @description xss防御自动装配
 * @date 2023-05-28
 */
@Configuration
@ConditionalOnProperty(prefix = "infrastructure.web.xss", value = "enable", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(XssProperties.class)
public class WebXssAutoConfiguration {
    @Resource
    private XssProperties xssProperties;

    /**
     * 配置跨站攻击 反序列化处理器
     *
     * @return
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer2() {
        return builder -> builder
                .deserializerByType(String.class, new XssStringJsonDeserializer());
    }


    /**
     * 配置跨站攻击过滤器
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean(new XssFilter());
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.setOrder(1);
        Map<String, String> initParameters = new HashMap<>(10);
        Collection<String> excludes = Lists.newArrayList("/favicon.ico", "/doc.html", "/swagger-ui.html", "/csrf", "/webjars/*", "/v2/*", "/swagger-resources/*", "/resources/*", "/static/*", "/public/*", "/classpath:*", "/actuator/*");
        if (CollUtil.isNotEmpty(xssProperties.getExclusionAddr())) {
            excludes = CollUtil.addAll(xssProperties.getExclusionAddr(), excludes);
        }
        initParameters.put("excludes", StringUtils.join(excludes, ","));
        filterRegistration.setInitParameters(initParameters);
        return filterRegistration;
    }
}

/**
 * 解决序列化和反序列化时，参数转换问题
 * addSerializer：序列化 （Controller 返回 给前端的json）
 * Long -> string
 * BigInteger -> string
 * BigDecimal -> string
 * date -> string
 * <p>
 * addDeserializer: 反序列化 （前端调用接口时，传递到后台的json）
 * 枚举类型: {"code": "xxx"} -> Enum
 */
//    @Bean
//    @Primary
//    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
//        builder.simpleDateFormat(DateUtil.DEFAULT_DATE_TIME_FORMAT);
//        ObjectMapper objectMapper = builder.createXmlMapper(false)
//                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
//                .timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
//                .build();
//
//        objectMapper
//                .setLocale(Locale.CHINA)
//                .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
//                //忽略未知字段
//                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
//                //该特性决定parser是否允许JSON字符串包含非引号控制字符（值小于32的ASCII字符，包含制表符和换行符）。 如果该属性关闭，则如果遇到这些字符，则会抛出异常。JSON标准说明书要求所有控制符必须使用引号，因此这是一个非标准的特性
//                .configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true)
//                // 忽略不能转移的字符
//                .configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true)
//                //单引号处理
//                .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
//                //日期格式
////                .setDateFormat(new SimpleDateFormat(DateUtil.DEFAULT_DATE_TIME_FORMAT));
//
//        //反序列化时，属性不存在的兼容处理
//        objectMapper.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//
//        SimpleModule simpleModule = new SimpleModule()
//                .addSerializer(Long.class, ToStringSerializer.instance)
//                .addSerializer(Long.TYPE, ToStringSerializer.instance);
//
//        return objectMapper.registerModule(simpleModule);
//    }